#include "AutoTesterEngine.h"
#include "mtalog.h"

CAutoTester* CAutoTester::self;

/**
 * Returns the sole instance of the class
 * */
CAutoTester* CAutoTester::GetInstance()
{
	if(self == 0)
	{
		TRAPD(err,self = NewL());
		
		if(err != KErrNone)
			return 0;
	}
	return self;
}

/**
 * Constructor
 * */
CAutoTester::CAutoTester() : CActive(EPriorityStandard), iCapturing(EFalse), iReplaying(EFalse), iArrayEmpty(EFalse), iScheduled(EFalse), iHandlesCount(0), iTimer(0), iSchedulerTimer(0)
{
	
}

/**
 * Destructor
 * */
CAutoTester::~CAutoTester()
{
	if(iCapturing)
		StopCapturing();
	
	Cancel();
	if(iTimer)
		delete iTimer;
	
	if(iSchedulerTimer)
		delete iSchedulerTimer;
	
	iTimer = 0;
	iSchedulerTimer = 0;
}

/**
 * Function to create the object of the engine
 * */
CAutoTester* CAutoTester::NewL()
{
	CAutoTester* self = new (ELeave) CAutoTester();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
}

/**
 * Second level constructor
 * */
void CAutoTester::ConstructL()
{
	CActiveScheduler::Add(this);
	iKeyData.Reset();
	User::LeaveIfError(iWsSession.Connect());
	iTimer = CAutoTimer::NewL();
	iSchedulerTimer = CSchedulerTimer::NewL();
}

/**
 * Starts the application
 * */
void CAutoTester::Start()
{
	CAknGlobalNote* note = CAknGlobalNote::NewL();
	CleanupStack::PushL(note);
	
	if(iCapturing)
	{	
		note->ShowNoteL(EAknGlobalInformationNote, _L("Capturing already in progress!"));
		CleanupStack::PopAndDestroy(1);
		return;
	}
	else
	{
		if(!iArrayEmpty)
		{
			iKeyData.Reset();
			iArrayEmpty = ETrue;
		}
		iTimer->ResetIndex();
		StartCapturingL();
		CreateSimulationEnv();
		BringDesktopToForegroud();
		iCapturing = ETrue;
		note->ShowNoteL(EAknGlobalInformationNote, _L("Capturing Started!"));
	}
	CleanupStack::PopAndDestroy(1);
}

/**
 * Stops the application
 * */
void CAutoTester::Stop()
{
	CAknGlobalNote* note = CAknGlobalNote::NewL();
	CleanupStack::PushL(note);
	
	if(iReplaying) // Stop pressed when replaying
	{
		iReplaying = EFalse;
		CleanupStack::PopAndDestroy(1);
		return;
	}
	
	if(iCapturing) // Stop pressed after capture command was issued (Valid case)
	{
		StopCapturing();
		iCapturing = EFalse;
		note->ShowNoteL(EAknGlobalInformationNote, _L("Capturing Stopped!"));
	}
	else
		note->ShowNoteL(EAknGlobalInformationNote, _L("Start Capturing First!"));
	
	CleanupStack::PopAndDestroy(1);
}

/**
 * Starts the key capturing process
 * */
void CAutoTester::StartCapturingL()
{
	iWindowGroup = RWindowGroup(iWsSession);
	User::LeaveIfError(iWindowGroup.Construct((TUint32)&iWindowGroup, EFalse));
	iWindowGroup.SetOrdinalPosition(-1);
	iWindowGroup.EnableReceiptOfFocus(EFalse);

	CApaWindowGroupName* wn=CApaWindowGroupName::NewLC(iWsSession);
	wn->SetHidden(ETrue);
	wn->SetWindowGroupName(iWindowGroup);
	CleanupStack::PopAndDestroy();

	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(48, 0, 0); // Numeric Zero
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(49, 0, 0); // Numeric One
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(50, 0, 0); // Numeric Two
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(51, 0, 0); // Numeric Three
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(52, 0, 0); // Numeric Four
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(53, 0, 0); // Numeric Five
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(54, 0, 0); // Numeric Six
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(55, 0, 0); // Numeric Seven
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(56, 0, 0); // Numeric Eight
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(57, 0, 0); // Numeric Nine

	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(42, 0, 0); // Asterisk key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(35, 0, 0); // Hash key

	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(EKeyYes, 0, 0); // Green key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(EKeyNo, 0, 0); // Red key

	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(63557, 0, 0); // Centre key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(63495, 0, 0); // Left key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(63496, 0, 0); // Right key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(63497, 0, 0); // Up key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(63498, 0, 0); // Down key

	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(EKeyDevice0, 0, 0); // Left soft key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(EKeyDevice1, 0, 0); // Right soft key

	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(63570, 0, 0); // Menu key
	iHandles[iHandlesCount++] = iWindowGroup.CaptureKey(8, 0, 0); // C key
	
	iWsSession.EventReady(&iStatus);
	SetActive();
}

/**
 * Stops the key capturing process
 * */
void CAutoTester::StopCapturing()
{
	if(iHandlesCount != 0)
	{
		for(TUint i=0; i<iHandlesCount; i++)
			iWindowGroup.CancelCaptureKey(iHandles[i]);
		
		Cancel();
		iHandlesCount = 0;
		iCapturing = EFalse;
	}
}

/**
 * Stores the generated event to the array
 * */
void CAutoTester::StoreKeyEvent(TUint& aCode, TInt& aScanCode, TUint& aModifiers, TInt& aRepeats, TUint& aWg)
{
	LOGARG("STORE : SCAN CODE = %d CODE = %d MODIFIERS = %d WG = %d",aScanCode, aCode, aModifiers, aWg);
	KeyData newData;
	newData.iCode = aCode;
	newData.iScanCode = aScanCode;
	newData.iModifiers = aModifiers;
	newData.iRepeats = aRepeats;
	newData.iWg = aWg;
	newData.iTimeSec = User::TickCount();
	iKeyData.Append(newData);
	
	if(iArrayEmpty)
		iArrayEmpty = EFalse;
}

/**
 * Simulate key using the "SendToWindowGroup" method
 * */
void CAutoTester::SimulateKey(TUint& aCode, TInt& aScanCode, TUint& aModifiers, TInt& aRepeats, TUint& aWg)
{
	LOGARG("SIMULATE : SCAN CODE = %d CODE = %d MODIFIERS = %d WG = %d",aScanCode, aCode, aModifiers, aWg);
	RWsSession wsSession = CCoeEnv::Static()->WsSession();
	LOGARG("FOCUS SCREEN = %d",wsSession.GetFocusScreen());
	TWsEvent event;
	event.SetType(EEventKey);
	event.SetTimeNow();
	event.Key()->iCode = aCode;
	event.Key()->iModifiers = aModifiers;
	event.Key()->iRepeats = aRepeats;
	event.Key()->iScanCode = aScanCode;
	wsSession.SendEventToWindowGroup( aWg, event );	
	wsSession.Flush();
}

/**
 * Simulate key using the RawEvent method
 * */
void CAutoTester::SimulateKey(TInt& aScanCode)
{
	LOGARG("SIMULATE : SCAN CODE = %d ",aScanCode);
	TRawEvent lEventDown;
	lEventDown.Set(TRawEvent::EKeyDown, aScanCode);
	UserSvr::AddEvent(lEventDown);
	TRawEvent lEventUp;
	lEventUp.Set(TRawEvent::EKeyUp, aScanCode);
	UserSvr::AddEvent(lEventUp);
}

/**
 * Simulate key using the direct simulation method
 * */
void CAutoTester::SimulateKey(TUint& aCode, TInt& aScanCode, TUint& aModifiers, TInt& aRepeats)
{
	LOGARG("SIMULATE : SCAN CODE = %d CODE = %d MODIFIERS = %d",aScanCode, aCode, aModifiers);
	RWsSession wsSession = CCoeEnv::Static()->WsSession();
	LOGARG("FOCUS SCREEN = %d",wsSession.GetFocusScreen());
	TKeyEvent keyEvent;
	keyEvent.iCode = aCode;  //member of TKeyCode
	keyEvent.iScanCode = aScanCode;
	keyEvent.iModifiers = aModifiers;
	keyEvent.iRepeats = aRepeats;
	wsSession.SimulateKeyEvent(keyEvent);
	wsSession.Flush();
}

/**
 * Brings the device's main screen to the foreground
 * */
void CAutoTester::BringDesktopToForegroud()
{
	TUid telephoneUid;
	telephoneUid.iUid = 0x100058b3;
	
	RWsSession session = CCoeEnv::Static()->WsSession();
	TApaTaskList* taskList = new TApaTaskList(session);
	TApaTask task = taskList->FindApp(telephoneUid);
	
	if(task.Exists())
		task.BringToForeground();
}

/**
 * Launches the "Telephone" application
 * */
void CAutoTester::LaunchAppL(const TUid aAppUid)
{
	RApaLsSession apaLsSession;
	User::LeaveIfError(apaLsSession.Connect());
	CleanupClosePushL(apaLsSession);

	TApaAppInfo appInfo;
	TInt retVal = apaLsSession.GetAppInfo(appInfo, aAppUid);

	if(retVal == KErrNone)
	{
		CApaCommandLine* cmdLine = CApaCommandLine::NewLC();
		cmdLine->SetExecutableNameL(appInfo.iFullName);
		cmdLine->SetCommandL(EApaCommandRun);
		apaLsSession.StartApp(*cmdLine);
		CleanupStack::PopAndDestroy(cmdLine);
	}
	else
	{
		// The application not found!
	}

	CleanupStack::PopAndDestroy(&apaLsSession);
}

/**
 * Replays the captured events
 * */
void CAutoTester::Replay()
{
	CAknGlobalNote* note = CAknGlobalNote::NewL();
	CleanupStack::PushL(note);
	
	if(iReplaying) // When stop is not issued and directly replayed is issued
	{
		iReplaying = EFalse;
		return;
	}
	
	if(iCapturing) // The user did not stop the "key capturing"
	{
		StopCapturing();
		iCapturing = EFalse;
	}
	
	if(!iArrayEmpty)
		iItemCount = iKeyData.Count();
		
	if(iItemCount == 0) // The user did not start the capturing process, nothing to be simulated
	{	
		note->ShowNoteL(EAknGlobalInformationNote, _L("No events to replay!"));
		CleanupStack::PopAndDestroy(1);
		return;
	}
	else
	{
		CreateSimulationEnv();
		BringDesktopToForegroud();
		iTimer->ResetIndex(); // Resetting the index in the timer class to avoid a panic (index out of bounds)
		iReplaying = ETrue;
		LOGARG("REPLAY ITEM COUNT = %d",iItemCount);
		iTimer->StartTimer(GetNextTimeDifference(0)); // This will start the simulation process		
	}
	CleanupStack::PopAndDestroy(1);
}

/**
 * The function simulates the key at the index of the event array
 * */
bool CAutoTester::SimulateKeyEvent(TUint aIndex)
{
	if(aIndex < iItemCount)
		SimulateKey(iKeyData[aIndex].iScanCode);
	
	if(aIndex == (iItemCount-2)) return true;
	
	return false;
}

/**
 * Returns the time difference to be used before generating next event 
 * */
TUint CAutoTester::GetNextTimeDifference(TUint aIndex)
{
	if(aIndex == 0)	return 0; // The time difference for the first event should be null
	
	TUint timeDiff = iKeyData[aIndex-1].iTimeSec - iKeyData[aIndex].iTimeSec;
	timeDiff /= 64; // Converting the time difference into seconds (human readable)
	timeDiff *= -1000000; // Converting the time difference into seconds (actual device)
	return timeDiff;
}

/**
 * Returns the value of the bool indicating if the keys are being captured 
 * */
TBool CAutoTester::IsCapturing()
{
	return iCapturing;
}

/**
 * Function that is called when a key is pressed
 * */
void CAutoTester::RunL()
{
	if( iStatus == KErrNone )
	{
		// EEventKey received
		TWsEvent we;
		iWsSession.GetEvent( we );
		if (we.Type() == EEventKey)
		{	
			TUint wgId = iWsSession.GetFocusWindowGroup();
					
			if(iCapturing)
				StoreKeyEvent(we.Key()->iCode, we.Key()->iScanCode, we.Key()->iModifiers, 
					we.Key()->iRepeats, wgId);
			
			iWsSession.SendEventToWindowGroup(wgId, we);
			iWsSession.EventReady( &iStatus );
			SetActive();
		}
	}
}

/**
 * Cancels any pending requests
 * */
void CAutoTester::DoCancel()
{
	iWsSession.EventReadyCancel();
}

/**
 * Sets the value of the replaying boolean
 * */
void CAutoTester::SetReplaying(TBool aValue)
{
	iReplaying = aValue;
}

/**
 * Returns the number of events in the array
 * */
TUint CAutoTester::GetArrayCount()
{
	if(!iArrayEmpty)
	{
		return iKeyData.Count();
	}
	return 0;
}

/**
 * Saves the generated events to a CSV file
 * */
void CAutoTester::SaveEvents()
{
	CAknGlobalNote* note = CAknGlobalNote::NewL();
	CleanupStack::PushL(note);
	if(iArrayEmpty)
	{
		note->ShowNoteL(EAknGlobalInformationNote, _L("No events to Save!"));
		CleanupStack::PopAndDestroy(1);
		return;
	}
	
	FILE *file = fopen("c:\\data\\autotester.dat", "wb"); /* append to the file in binary mode */
	if ( file != NULL )
	{
		TInt itemCount = iKeyData.Count();
		
		for(TInt index = 0; index < itemCount; index++)
		{
			fwrite(&iKeyData[index], sizeof(iKeyData[index]), 1, file);
		}
		
		fclose(file);
	}
	else
	{
		note->ShowNoteL(EAknGlobalInformationNote, _L("Error: Save Failed!"));
		CleanupStack::PopAndDestroy(1);
		return;
	}
	note->ShowNoteL(EAknGlobalInformationNote, _L("Saved!"));
	CleanupStack::PopAndDestroy(1);
}

/**
 * Reloads the generated events from a CSV file
 * */
void CAutoTester::ReloadEvents()
{
	if(iCapturing)
	{
		StopCapturing();
	}

	CAknGlobalNote* note = CAknGlobalNote::NewL();
	CleanupStack::PushL(note);
		
	if(iScheduled) // Cancelling the scheduled timer
	{
		note->ShowNoteL(EAknGlobalInformationNote, _L("Scheduled replay cancelled!"));
		iSchedulerTimer->Cancel();
		iScheduled = EFalse;
	}
	
	if(!iArrayEmpty)
	{
		iKeyData.Reset();
		iArrayEmpty = ETrue;
	}
	
	FILE *file = fopen("c:\\data\\autotester.dat", "rb"); /* read from the file in binary mode */
	
	if ( file != NULL )
	{	
		KeyData keyData;
		
		while ( fread(&keyData, sizeof(keyData), 1, file) == 1 ) /* read all records */
		{
			iKeyData.Append(keyData);
		}
		iArrayEmpty = EFalse;
		note->ShowNoteL(EAknGlobalInformationNote, _L("Reload Complete!"));
		CleanupStack::PopAndDestroy(1);
	
		LOGARG("%d EVENTS RELOADED.",iKeyData.Count());
	
		fclose(file);
	}
	else
	{
		note->ShowNoteL(EAknGlobalInformationNote, _L("Error: Reload Failed!"));
		CleanupStack::PopAndDestroy(1);
		return;
	}
}

/**
 * Schedules a replay of the events
 * */
void CAutoTester::ScheduleReplay(TUint aHours)
{
	if(iCapturing) // When stop is not issued and directly schedule is issued
		StopCapturing();
	
	iSchedulerTimer->StartTimer(aHours);
	
	iScheduled = ETrue;
	
	CAknGlobalNote* note = CAknGlobalNote::NewL();
	CleanupStack::PushL(note);
	note->ShowNoteL(EAknGlobalInformationNote, _L("Replay Scheduled!"));
	CleanupStack::PopAndDestroy(1);
}

/**
 * Creates the environment required for key simulation / replay; Kills all tasks (except system tasks) launched during event capturing
 * */
void CAutoTester::CreateSimulationEnv()
{
	TUid telephoneUid;
	telephoneUid.iUid = 0x100058b3;

	TUid standbyUid;
	standbyUid.iUid = 0x101fd64c;
	
	TUid selfUid;
	selfUid.iUid = 0xE447FCF4;
	
	RApaLsSession apaSession;
	TInt err = apaSession.Connect();
	
	if(err == KErrNone)
	{
		RWsSession session = CCoeEnv::Static()->WsSession();
		TApaTaskList* taskList = new TApaTaskList(session);
		CleanupStack::PushL(taskList);
		
		TInt appCount;
		apaSession.AppCount(appCount);
		LOGARG("APP COUNT = %d",appCount);
		apaSession.GetAllApps();
		
		for(TInt index = 1; index <= appCount; index++)
		{
			TApaAppInfo appInfo;
			apaSession.GetNextApp(appInfo);
			
			// Skipping the Telephone, Standby and our application
			if(appInfo.iUid == telephoneUid || appInfo.iUid == standbyUid || appInfo.iUid == selfUid)
			{
				continue;
			}
			
			TApaTask task = taskList->FindApp(appInfo.iUid);
						
			if(task.Exists())
			{
				CApaWindowGroupName *wgName = CApaWindowGroupName::NewL(session, task.WgId());
				CleanupStack::PushL(wgName);
				
				if(wgName->IsSystem())
				{
					continue;
				}
				
				CleanupStack::PopAndDestroy( wgName );
				task.EndTask();
			}
		}
		CleanupStack::PopAndDestroy(1);
	}
}
